﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Galaktika.BI.Runtime.Services;
using System.Globalization;
using Galaktika.BI.Extensibility;
using Galaktika.BI.Silverlight.Controls.DateChoice.ClientServer;
using Ranet.Olap.Core.Data;
using Galaktika.BI.Silverlight.Services;

namespace Galaktika.BI.Silverlight.Controls.DateChoice
{
    public partial class DateChoiceControl : UserControl
    {
        public DateChoiceControl()
        {
            InitializeComponent();

            calendar.SelectionMode = CalendarSelectionMode.SingleDate;
            calendar.SelectedDatesChanged += new EventHandler<SelectionChangedEventArgs>(calendar_SelectedDatesChanged);

            calendar.DisplayDateChanged += new EventHandler<CalendarDateChangedEventArgs>(calendar_DisplayDateChanged);
            Galaktika.BI.Silverlight.MemoryProfiler.AddReference(this);
        }

        void calendar_DisplayDateChanged(object sender, CalendarDateChangedEventArgs e)
        {
            RefreshCurrentCalendar();
        }

        public void Initialize()
        {
            calendar.DisplayDate = DateTime.Today;
            RefreshCurrentCalendar(); 
        }

        public IBindingContext Context { get; set; }
        
        #region Свойства для настройки на OLAP
        /// <summary>
        /// 
        /// </summary>
        private String m_Connection = String.Empty;
        /// <summary>
        /// 
        /// </summary>
        public String Connection
        {
            get
            {
                return m_Connection;
            }
            set
            {
                m_Connection = value;
            }
        }

        /// <summary>
        /// Имя OLAP куба
        /// </summary>
        String m_CubeName = String.Empty;
        /// <summary>
        /// Имя OLAP куба
        /// </summary>
        public String CubeName
        {
            get
            {
                return this.m_CubeName;
            }
            set
            {
                m_CubeName = value;
            }
        }
        #endregion Свойства для настройки на OLAP

        String m_MonthUniqueNameScript = String.Empty;
        public String MonthUniqueNameScript
        {
            get
            {
                return this.m_MonthUniqueNameScript;
            }
            set
            {
                m_MonthUniqueNameScript = value;
            }
        }

        String m_DayUniqueNameScript = String.Empty;
        public String DayUniqueNameScript
        {
            get
            {
                return this.m_DayUniqueNameScript;
            }
            set
            {
                m_DayUniqueNameScript = value;
            }
        }

        #region Функции полечения уникальных имен по дате
        public String GetMonthUniqueName(DateTime dt)
        {
            IScriptEngine script = (IScriptEngine)this.Context.Services.GetService(typeof(IScriptEngine));
            if (script != null)
            {
                FuncInvokeCheckResult check = script.CheckFuncInvoke(MonthUniqueNameScript);
                if (check.IsSuccess)
                {
                    check.Parameters = new string[] { dt.ToString(CultureInfo.InvariantCulture) };
                    return script.InvokeFunction(this.Context, check);
                }
                else
                {
                    throw new InformationalException(Localization.Exc_DateParameterMonth + MonthUniqueNameScript);
                }
            }

            return string.Empty;
            /*
            //System.DateTime dt = System.DateTime.Parse(args[0], System.Globalization.CultureInfo.InvariantCulture);
            string res = String.Empty;
            try
            {
                //---------------- Шаблон для месяца: [Период].[ГКМД].[Год].&[2005].&[20051].&[200501] -----------
                //Путь, который вычислять не надо
                String path = "[Период].[ГКМД].[Год]";
                //Год в формате yyyy
                String Year = dt.Year.ToString();                           
                //Квартал в формате yyyyq
                String Quarter = Year + ((int)Math.Ceiling(dt.Month / 3.0)).ToString();      
                //Месяц в формате yyyymm
                String Month = Year;
                if(dt.Month.ToString().Length == 1)
                    Month = Month  + "0";
                Month = Month  + dt.Month.ToString();
                
                res = path + ".&[" + Year + "].&[" + Quarter + "].&[" + Month + "]";
                return res;
            }
            catch
            {
                return String.Empty;
            }*/
        }

        public String GetDayUniqueName(DateTime dt)
        {
            IScriptEngine script = (IScriptEngine)this.Context.Services.GetService(typeof(IScriptEngine));
            if (script != null)
            {
                FuncInvokeCheckResult check = script.CheckFuncInvoke(DayUniqueNameScript);
                if (check.IsSuccess)
                {
                    check.Parameters = new string[] { dt.ToString(CultureInfo.InvariantCulture) };
                    return script.InvokeFunction(this.Context, check);
                }
                else
                {
                    throw new InformationalException(Localization.Exc_DateParameterDay + DayUniqueNameScript);
                }
            }

            return string.Empty;
            //System.DateTime dt = System.DateTime.Parse(args[0], System.Globalization.CultureInfo.InvariantCulture);
            /*
            string res = String.Empty;
            try
            {
                //---------------- Шаблон для дня: [Период].[ГКМД].[Год].&[2005].&[20051].&[200501].&[20050103] -----------
                //Путь, который вычислять не надо
                String path = "[Период].[ГКМД].[Год]";
                //Год в формате yyyy
                String Year = dt.Year.ToString();
                //Квартал в формате yyyyq
                String Quarter = Year + ((int)Math.Ceiling(dt.Month / 3.0)).ToString();
                //Месяц в формате yyyymm
                String Month = Year;
                if (dt.Month.ToString().Length == 1)
                    Month = Month + "0";
                Month = Month + dt.Month.ToString();
                //День в формате yyyymmdd
                String Day = Month;
                if (dt.Day.ToString().Length == 1)
                    Day = Day + "0";
                Day = Day + dt.Day.ToString();

                res = path + ".&[" + Year + "].&[" + Quarter + "].&[" + Month + "].&[" + Day + "]";
                return res;
            }
            catch
            {
                return String.Empty;
            }
            */
        }
        #endregion Функции полечения уникальных имен по дате

        List<String> m_LoadedMonths = new List<string>();
        //Дата, первая видимая в календаре. Будем использовать чтобы повторно не зачитывать уже имеющиеся данные
        DateTime lastDrawingDate = DateTime.MinValue;
        private void RefreshCurrentCalendar()
        {
            try
            {
                DateTime firstDate = calendar.DisplayDate;

                //Проверяем - читали ли в прошлый раз эти же данные
                if (firstDate != lastDrawingDate)
                {
                    //Загружаем данные по месяцам
                    LoadDates(firstDate);
                    UpdateDates();
                    lastDrawingDate = firstDate;
                }
            }
            finally
            {
            }
        }

        Dictionary<DateTime, String> m_Dates = new Dictionary<DateTime, string>();

        void UpdateDates()
        {
            calendar.BlackoutDates.Clear();

            DateTime tmp;
            DateTime averageDate = new DateTime(calendar.DisplayDate.Year, calendar.DisplayDate.Month, 1);
            tmp = averageDate.AddMonths(-1);
            DateTime firstDate = new DateTime(tmp.Year, tmp.Month, 15);
            tmp = averageDate.AddMonths(1);
            DateTime lastDate = new DateTime(tmp.Year, tmp.Month, 15);

            DateTime date = firstDate;
            while (date <= lastDate)
            {
                String un = String.Empty;
                if (m_Dates.ContainsKey(date))
                {
                    un = m_Dates[date];
                }
                else
                {
                    un = GetDayUniqueName(date);
                    m_Dates[date] = un;
                }
                
                if(!m_LoadedDays.Contains(un))
                    calendar.BlackoutDates.Add(new CalendarDateRange(date)); 
                date = date.AddDays(1);
            }
        }

        void LoadDates(DateTime date)
        {
            DateTime averageDate = new DateTime(date.Year, date.Month, 1);
            DateTime firstDate = averageDate.AddMonths(-1);
            //Средняя из отображаемых дата (примерно) - для вычисления месяца, который собственно и является главным отображаемым в календаре
            //DateTime averageDate = firstDate.AddDays(21);
            //Последняя отображаемая дата (тоже примерно) - для вычисления месяца, следующего за отображаемым
            DateTime lastDate = averageDate.AddMonths(1);
            //Текущая дата - для вычисления текущего месяца
            DateTime todayDate = DateTime.Now;

            //Формируем уникальные имена для месяцев
            string firstMonthUniqueName = String.Empty;
            string averageMonthUniqueName = String.Empty;
            string lastMonthUniqueName = String.Empty;
            string todayMonthUniqueName = String.Empty;

            firstMonthUniqueName = this.GetMonthUniqueName(firstDate);
            averageMonthUniqueName = this.GetMonthUniqueName(averageDate);
            lastMonthUniqueName = this.GetMonthUniqueName(lastDate);
            todayMonthUniqueName = this.GetMonthUniqueName(todayDate);

            //Проверяем чтобы уникальные имена сформировались корректно
            if (String.IsNullOrEmpty(firstMonthUniqueName) ||
                String.IsNullOrEmpty(averageMonthUniqueName) ||
                String.IsNullOrEmpty(lastMonthUniqueName) ||
                String.IsNullOrEmpty(todayMonthUniqueName))
            {
                MessageBox.Show(Localization.Exc_DateParameterMonth);
                return;
            }

            //Проверяем нужно ли грузить данные за текущий месяц
            //Текущий месяц может быть среди отображаемых в текущий момент - в этом случае информацию о нем грузить не надо
            bool loadTodayMonth = true;
            if (firstMonthUniqueName == todayMonthUniqueName ||
                averageMonthUniqueName == todayMonthUniqueName ||
                lastMonthUniqueName == todayMonthUniqueName)
                loadTodayMonth = false;

            //Проверяем загружались ли данные для указанных месяцев
            bool firstLoaded = DataIsLoaded(firstMonthUniqueName);
            bool averageLoaded = DataIsLoaded(averageMonthUniqueName);
            bool lastLoaded = DataIsLoaded(lastMonthUniqueName);
            bool todayLoaded = DataIsLoaded(todayMonthUniqueName);

            if (firstLoaded && averageLoaded && lastLoaded && todayLoaded)
            {
                return;
            }

            InvokeSchema schema = new InvokeSchema(Commands.CommandId.DateChoiceCommandId);
            schema.Args.Add(new InvokeArg(KnownInvokeArgs.ContextId, (string)this.Context.UserData["_$Id"]));
            schema.Args.Add(new InvokeArg(KnownInvokeArgs.ConnectionId, m_Connection));

            DateChoiceQueryArgs args = new DateChoiceQueryArgs();
            args.CubeName = CubeName;
            if (!firstLoaded)
            {
                args.MonthUniqueNames.Add(firstMonthUniqueName);
                m_LoadedMonths.Add(firstMonthUniqueName);
            }
            if (!averageLoaded)
            {
                args.MonthUniqueNames.Add(averageMonthUniqueName);
                m_LoadedMonths.Add(averageMonthUniqueName);
            }
            if (!lastLoaded)
            {
                args.MonthUniqueNames.Add(lastMonthUniqueName);
                m_LoadedMonths.Add(lastMonthUniqueName);
            }
            //Текущий месяц может быть среди отображаемых в текущий момент - в этом случае информацию о нем грузить не надо
            if (!todayLoaded && loadTodayMonth)
            {
                args.MonthUniqueNames.Add(todayMonthUniqueName);
                m_LoadedMonths.Add(todayMonthUniqueName);
            }

            schema.Args.Add(new InvokeArg(KnownInvokeArgs.DateChoiceQueryInfo, XmlUtility.Obj2XmlStr(args, Common.Namespace)));

            /*
            DataServiceSoapClient service = ServiceManager.CreateService<DataServiceSoapClient, DataServiceSoap>();
            service.InvokeCommandCompleted += new EventHandler<InvokeCommandCompletedEventArgs>(service_InvokeCommandCompleted);
            service.InvokeCommandAsync(schema);
            */
        }

        void service_InvokeCommandCompleted(object sender, InvokeCommandCompletedEventArgs e)
        {
            if (e.Error != null)
            {
                MessageBox.Show(e.Error.ToString());
                return;
            }

            // В случае листания месяцев будут приходить данные только по одному месяцу, т.к. по 2 остальным уже приходили на предыдущем шаге
            List<MemberDataWrapper> members = XmlUtility.XmlStr2Obj<List<MemberDataWrapper>>(e.Result.Content);
            if (members != null)
            {
                foreach (MemberDataWrapper info in members)
                {
                    if (!m_LoadedDays.Contains(info.Member.UniqueName))
                    {
                        m_LoadedDays.Add(info.Member.UniqueName);
                    }
                }
            }
            
            UpdateDates();
        }

        /// <summary>
        /// Возвращает true если данные для указанного месяца уже загружались в Кэш
        /// </summary>
        /// <param name="UniqueName"></param>
        /// <returns></returns>
        private bool DataIsLoaded(string UniqueName)
        {
            //Ищем в списке родителей, чьи дочерние уже грузились
            return m_LoadedMonths.Contains(UniqueName);
        }

        /// <summary>
        /// Кэш загруженных дат
        /// </summary>
        List<String> m_LoadedDays = new List<string>();

        void calendar_SelectedDatesChanged(object sender, SelectionChangedEventArgs e)
        {
            
        }
    }
}
